package com.demo.common.excel;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.demo.common.utils.JsonUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * excelUtils
 */
@Slf4j
public class ExcelUtils {

    /**
     * 导入转换器
     */
    private static Map<String, Function> importConvertMap=new ConcurrentHashMap<>();
    /**
     * 导出转换器
     */
    private static Map<String, Function> exportConvertMap=new ConcurrentHashMap<>();

    /**
     * 获取导入模板
     * @param convertName
     * @return
     */
    public static Function getImportConvert(String convertName){
        return importConvertMap.getOrDefault(convertName,Function.identity());
    }

    private static ThreadLocal<CellMergeStrategy> cellMergeStrategyThreadLocal=new ThreadLocal<>();

    public static void setCellMergeStrategy(CellMergeStrategy cellMergeStrategy){
        cellMergeStrategyThreadLocal.set(cellMergeStrategy);
    }

    public static CellMergeStrategy getCellMergeStrategy(){
        return cellMergeStrategyThreadLocal.get();
    }

    /**
     * 获取导出模板
     * @param convertName
     * @return
     */
    public static Function getExportConverter(String convertName){
        return exportConvertMap.getOrDefault(convertName,Function.identity());
    }

    /**
     * 注册导入转换器
     * @param convertName
     * @param function
     */
    public static void registerImportConvert(String convertName,Function function) {
        Assert.notNull(convertName,"转换器名称不能为空");
        Assert.notNull(function,"转换器不能为空");
        Function put = importConvertMap.put(convertName, function);
        if(put!=null){
            log.warn("excel导入转换器名称重复,重复的转换器是{},原始转换器{}",convertName,function);
        }
    }

    /**
     * 注册导出转换器
     */
    public static void registerExportConvert(String convertName,Function function) {
        Assert.notNull(convertName,"转换器名称不能为空");
        Assert.notNull(function,"转换器不能为空");
        Function put = exportConvertMap.put(convertName, function);
        if(put!=null){
            log.warn("excel导出转换器名称重复,重复的转换器是{},原始转换器{}",convertName,function);
        }
    }

    /**
     * 注册简单转换器
     * @param convertName
     * @param simpleConvert
     */
    public static void registerSimpleConvert(String convertName,String simpleConvert){
        String[] split = simpleConvert.split(",");
        try {
            Map<String,String> exportMap=new HashMap<>();
            for (String temp : split) {
                String[] inner = temp.split("=");
                exportMap.put(inner[0],inner[1]);
            }
            registerExportConvert(convertName+"_export",(it)->exportMap.get(it.toString()));
            Map<String, String> importMap = exportMap.entrySet().stream().collect(Collectors.toMap(it -> it.getValue(), it -> it.getKey()));
            registerImportConvert(convertName+"_import",(it)->importMap.get(it));
        }catch (Exception e){
            log.warn("excel注入简单转换器失败,解析失败的表达式{}",simpleConvert);
        }
    }

    /**
     * 生成导入模板
     * @param headers
     * @param response
     */
    @SneakyThrows
    public static void createTemplate(List<List<String>> headers, HttpServletResponse response){
        response.setHeader("Content-disposition", "attachment;filename=template.xlsx");
        ExcelWriter excelWriter = EasyExcelFactory.write().file(response.getOutputStream()).head(headers).build();
        excelWriter.write(null,new WriteSheet());
        excelWriter.finish();
    }

    /**
     * 读取导入数据
     * @return
     */
    @SneakyThrows
    public static List readImportData(List<FieldWrapper> fieldWrappers,InputStream in){
        List<List<String>> headers = fieldWrappers.stream().map(it -> Collections.singletonList(it.getName())).collect(Collectors.toList());
        ExcelSimpleListener readListener = new ExcelSimpleListener(fieldWrappers);
        ExcelReader reader = EasyExcelFactory.read(in).excelType(ExcelTypeEnum.XLSX).head(headers).registerReadListener(readListener).build();
        reader.read(new ReadSheet());
        reader.finish();
        return readListener.getData();
    }

    /**
     * 一次性导出
     * @param fieldWrappers
     * @param data
     * @param response
     */
    @SneakyThrows
    public static void onceExport(List<FieldWrapper> fieldWrappers, List data, HttpServletResponse response){
        response.setHeader("Content-disposition", "attachment;filename=export.xlsx");
        List<List<String>> headers = fieldWrappers.stream().map(it -> Collections.singletonList(it.getName())).collect(Collectors.toList());
        ExcelWriter excelWriter = EasyExcelFactory.write().file(response.getOutputStream()).head(headers).build();
        List excelData=convert2ExcelData(fieldWrappers, data);
        excelWriter.write(excelData,new WriteSheet());
        excelWriter.finish();
    }

    /**
     * 导出数据转换
     * @param fieldWrappers
     * @param data
     * @return
     */
    public static List convert2ExcelData(List<FieldWrapper> fieldWrappers, List data){
        List excelData=new ArrayList(data.size());
        for (Object oriData : data) {
            Map<String,Object> map = JsonUtil.json2Object(JsonUtil.object2Json(oriData), Map.class);
            List lineData=new ArrayList();
            for (FieldWrapper fieldWrapper : fieldWrappers) {
                String name = fieldWrapper.getField().getName();
                Object value = map.get(name);
                if(value==null){
                    value="";
                }else if(fieldWrapper.getExportConverter()!=null){
                    value=ExcelUtils.getExportConverter(fieldWrapper.getExportConverter()).apply(value);
                }
                lineData.add(value);
            }
            excelData.add(lineData);
        }
        return excelData;
    }

}
